#pragma once

#include "sqlite/sqlite3.h"

#include <vector>
#include <gtl/string/str.h>

namespace gtl
{

class sqlite_err_msg
{
public:
	sqlite_err_msg()
	{
		m_err_msg = NULL;
	}

	~sqlite_err_msg()
	{
		reset();
	}

	sqlite_err_msg& reset()
	{
		if(m_err_msg != NULL)
			sqlite3_free(m_err_msg);
		m_err_msg = NULL;
		return *this;
	}

	operator char**()
	{
		return &m_err_msg;
	}

	operator const char*()
	{
		return m_err_msg;
	}

#ifdef _UNICODE
	operator const wchar_t*()
	{
		m_err_msg_tmp = gtl::str(m_err_msg).trans();
		return m_err_msg_tmp;
	}
#endif

private:
	char* m_err_msg;

#ifdef _UNICODE
	gtl::tstr m_err_msg_tmp;
#endif
};

struct sqlite_result_set
{
	typedef std::vector<gtl::tstr>   fields_type;
	typedef std::vector<gtl::tstr>   record_type;
	typedef std::vector<record_type> records_type;

	fields_type  fields;
	records_type records;
};

class sqlite
{
public:
	sqlite(void)
	{
		m_sqlite = NULL;
	}

	virtual ~sqlite(void)
	{
		close();
	}

public:
	int open(const gtl::tchar* file_path)
	{
		if(m_sqlite != NULL)
			return -1;

#ifdef _UNICODE
		return sqlite3_open16(file_path, &m_sqlite);
#else
		return sqlite3_open(file_path, &m_sqlite);
#endif
	}

	int close()
	{
		if(m_sqlite == NULL)
			return -1;

		int ret = sqlite3_close(m_sqlite);
		m_sqlite = NULL;
		return ret;
	}

	void free(void* value)
	{
		sqlite3_free(value);
	}

	int select(const gtl::tchar* sql)
	{
		return execute(sql, select_callback, NULL, NULL);
	}

	int select(const gtl::tchar* sql, sqlite_err_msg& err_msg)
	{
		return execute(sql, select_callback, NULL, err_msg);
	}

	int select(const gtl::tchar* sql, sqlite_result_set& rs)
	{
		return execute(sql, select_callback, &rs, NULL);
	}

	int select(const gtl::tchar* sql, sqlite_result_set& rs, sqlite_err_msg& err_msg)
	{
		return execute(sql, select_callback, &rs, err_msg);
	}

	int execute(const gtl::tchar* sql)
	{
		return execute(sql, select_callback, NULL, NULL);
	}

	int execute(const gtl::tchar* sql, sqlite_err_msg& err_msg)
	{
		return execute(sql, select_callback, NULL, err_msg);
	}

	int execute(const gtl::tchar* sql, sqlite3_callback fun_callback, void* arg, char** err_msg)
	{
		if(sql == NULL || m_sqlite == NULL)
			return -1;

#ifdef _UNICODE
		return sqlite3_exec(m_sqlite, gtl::tstr(sql).utf8(), fun_callback, arg, err_msg);
#else
		return sqlite3_exec(m_sqlite, sql, fun_callback, arg, err_msg);
#endif
	}

	int begin_transaction()
	{
		return execute(_T("begin transaction"), NULL, NULL, NULL);
	}

	int commit_transaction()
	{
		return execute(_T("commit transaction"), NULL, NULL, NULL);
	}

	int rollback_transaction()
	{
		return execute(_T("rollback transaction"), NULL, NULL, NULL);
	}

	gtl::tstr get_err_msg()
	{
		const char* pszErrMsg = sqlite3_errmsg(m_sqlite);
		if(pszErrMsg == NULL)
			return gtl::tstr();

		return gtl::tstr(pszErrMsg);
	}

public:
	sqlite3* get()
	{
		return m_sqlite;
	}

protected:
	static int select_callback(void* arg, int argc, char** values, char** columns)
	{
		sqlite_result_set* rs = (sqlite_result_set*)arg;
		if(rs == NULL)
			return 0;

		if(argc <= 0)
			return 0;

		if(rs->fields.empty())
		{
			for(int i = 0; i < argc; ++i)
			{
				rs->fields.push_back(gtl::tstr_warp(gtl::str(columns[i]).trans(CP_UTF8)));
			}
		}

		rs->records.push_back(sqlite_result_set::record_type());
		sqlite_result_set::record_type& record = rs->records.back();
		for(int i = 0; i < argc; ++i)
		{
			record.push_back(gtl::tstr_warp(gtl::str(values[i]).trans(CP_UTF8)));
		}

		return 0;
	}

protected:
	sqlite3* m_sqlite;
};

class sqlite_transaction
{
public:
	sqlite_transaction(sqlite& sqlit) : m_sqlite(sqlit)
	{
		m_sqlite.begin_transaction();
	}

	~sqlite_transaction()
	{
		m_sqlite.commit_transaction();
	}

private:
	sqlite& m_sqlite;
};

} // end namespace gtl
